﻿using System.Linq;
using Sustainalytics.Reporting.Framework;
using Foxit.PDF.Merger;
using System.Collections.Generic;
using Foxit.PDF;
using Foxit.PDF.PageElements;
using Foxit.PDF.Imaging;
using Sustainalytics.Utils;
using Sustainalytics.Entities;
using System;

namespace Sustainalytics.RiskRatings.Reporting
{
    public class CompanyReportGenerator 
    {
        # region Static Images

        private static byte[] _companyLogoFooterImage;
        public byte[] CompanyLogoFooterImage { get { return _companyLogoFooterImage; } }

        private static void LoadStaticImages()
        {
            if (_companyLogoFooterImage == null)
                _companyLogoFooterImage = ImageManager.GetJpgImage(@"Images\sustaynalytics_logo_footer.jpg");
        }

        #endregion

        static CompanyReportGenerator()
        {
            LoadStaticImages();
        }


        public GeneratedPdfReport GeneratePdfReport(ReportDataSource ds, bool onlyMerge = false)
        {
            var report = new PdfReport();
            report.Document.Title = "Risk Rating Company Report";

            var reportSectionGenerators = new PdfReportSectionGenerator[]
            {
                new CompanyOverviewSection(1),
                new MaterialIssuesOverviewSection(2),
                new CorporateGovernanceSection(3),
                new MaterialEsgIssuesSection(4),
                new AppendixSection(5),
                new FinalSection(6)
            };

            var sections = new List<PdfReportSection>();

            var actions = reportSectionGenerators.Select((generator) => new System.Action(() =>
                {
                    if (!(ds.ReportType == ReportType.SummaryRiskRatingsReport &&
                    new[] { typeof(MaterialEsgIssuesSection), typeof(CorporateGovernanceSection), typeof(AppendixSection) }.Contains(generator.GetType())))
                        sections.Add(generator.GeneratePdfReportSection(ds));
                }
                ));

            ParallelHelper.RunInParallel(actions,1);

            var result = new GeneratedPdfReport
            {
                Id = ds.CompanyId,
                Name = ds.CompanyProfile.CompanyName,
                PdfDocument = new byte[0],
                Sections = new List<PdfReportSection>()
            };

            PdfDocument pdfDocument;
            OutlinePosition _rootOutlinePositions = new OutlinePosition();
            _rootOutlinePositions.ChildOutlinePositions = new List<OutlinePosition>();
            int curentReportPageCount = 0;
            List<Page> meiPages = new List<Page>();
            int cgOverviewPageNo = 0;
            if (sections.Count > 0)
            {
                foreach (var section in sections.OrderBy(x => x.Index))
                {
                    if (section?.PdfDocument.Length > 0)
                    {
                        foreach (OutlinePosition parentOutlinePosition in section.OutlinePositions)
                        {
                            var parentOutline = report.ParentOutline.ChildOutlines.Add(parentOutlinePosition.Name,
                                new XYDestination(curentReportPageCount + parentOutlinePosition.PageNumber, 0.0f,
                                    parentOutlinePosition.VerticalPosition));
                            parentOutlinePosition.PageNumber += curentReportPageCount;

                            if (parentOutlinePosition.ChildOutlinePositions != null && parentOutlinePosition.ChildOutlinePositions.Any())
                            {
                                foreach (OutlinePosition childOutlinePosition in parentOutlinePosition.ChildOutlinePositions)
                                {
                                    parentOutline.ChildOutlines.Add(childOutlinePosition.Name,
                                        new XYDestination(curentReportPageCount + childOutlinePosition.PageNumber, 0.0f,
                                            childOutlinePosition.VerticalPosition));

                                    if (childOutlinePosition.ChildOutlinePositions != null && childOutlinePosition.ChildOutlinePositions.Any())
                                    {
                                        foreach (OutlinePosition grandChildOutlinePosition in childOutlinePosition.ChildOutlinePositions)
                                        {
                                            grandChildOutlinePosition.PageNumber += curentReportPageCount;
                                        }
                                    }
                                    childOutlinePosition.PageNumber += curentReportPageCount;
                                }
                            }
                        }
                        _rootOutlinePositions.ChildOutlinePositions.AddRange(section.OutlinePositions);

                        pdfDocument = new PdfDocument(section.PdfDocument);
                        for (var pageNo = 1; pageNo <= pdfDocument.Pages.Count; pageNo++)
                        {
                            var page = new ImportedPage(pdfDocument.GetPage(pageNo));
                            if (section.Index == 3 && cgOverviewPageNo == 0)
                                cgOverviewPageNo = curentReportPageCount;
                            if (section.Index == 4)
                                meiPages.Add(page);
                            report.AddPage(page);
                            curentReportPageCount++;
                        }
                        result.Sections.Add(section);
                    }
                }

                if (ds.ReportType != ReportType.SummaryRiskRatingsReport)
                {
                    var companyOverviewPageReport = report.Document.Pages[0];
                    LinkCompanyOverviewElements(companyOverviewPageReport, reportSectionGenerators[0].PageElementPositions, _rootOutlinePositions.ChildOutlinePositions);

                    var corporateGovernancePageReport = report.Document.Pages[cgOverviewPageNo + 1];
                    LinkCorporateGovernanceElements(corporateGovernancePageReport, reportSectionGenerators[2].PageElementPositions, _rootOutlinePositions.ChildOutlinePositions);

                    LinkMeiElements(meiPages, reportSectionGenerators[3].PageElementPositions, _rootOutlinePositions.ChildOutlinePositions, false);
                }
            }
            AddPageFooter(report);
            if (report.PageCounter > 0)
            {
                result.PdfDocument = report.Draw();
            }
            return result;
        }

        public GeneratedPdfReport GenerateCorePdfReport(ReportDataSource ds, bool onlyMerge = false)
        {
            var report = new PdfReport();
            report.Document.Title = "Core Risk Rating Company Report";
            var reportSectionGenerators = new PdfReportSectionGenerator[]
            {
                new CompanyOverviewSection(1),
                new MaterialIssuesOverviewSection(2),
                new CoreAppendixSection(3),
                new FinalSection(4)
            };
            var sections = new List<PdfReportSection>();
            var actions = reportSectionGenerators.Select((generator) => new System.Action(() =>
            {
                if (ds.ReportType != ReportType.SummaryCoreRiskRatingsReport || generator.GetType() != typeof(CoreAppendixSection))
                    sections.Add(generator.GeneratePdfReportSection(ds));
            }));

            ParallelHelper.RunInParallel(actions);
            var result = new GeneratedPdfReport
            {
                Id = ds.CompanyId,
                Name = ds.CompanyProfile.CompanyName,
                PdfDocument = new byte[0],
                Sections = new List<PdfReportSection>()
            };
            PdfDocument pdfDocument;
            OutlinePosition _rootOutlinePositions = new OutlinePosition();
            _rootOutlinePositions.ChildOutlinePositions = new List<OutlinePosition>();
            List<Page> meiPages = new List<Page>();
            int curentReportPageCount = 0;
            if (sections.Count > 0)
            {
                foreach (var section in sections.OrderBy(x => x.Index))
                {
                    if (section?.PdfDocument.Length > 0)
                    {
                        foreach (OutlinePosition parentOutlinePosition in section.OutlinePositions)
                        {
                            var parentOutline = report.ParentOutline.ChildOutlines.Add(parentOutlinePosition.Name,
                                new XYDestination(curentReportPageCount + parentOutlinePosition.PageNumber, 0.0f,
                                    parentOutlinePosition.VerticalPosition));
                            parentOutlinePosition.PageNumber += curentReportPageCount;

                            if (parentOutlinePosition.ChildOutlinePositions != null && parentOutlinePosition.ChildOutlinePositions.Any())
                            {
                                foreach (OutlinePosition childOutlinePosition in parentOutlinePosition.ChildOutlinePositions)
                                {
                                    childOutlinePosition.PageNumber += curentReportPageCount;
                                }
                            }
                        }
                        _rootOutlinePositions.ChildOutlinePositions.AddRange(section.OutlinePositions);

                        pdfDocument = new PdfDocument(section.PdfDocument);
                        for (var pageNo = 1; pageNo <= pdfDocument.Pages.Count; pageNo++)
                        {
                            var page = new ImportedPage(pdfDocument.GetPage(pageNo));
                            report.AddPage(page);
                            if (section.Index == 2)
                                meiPages.Add(page);
                            curentReportPageCount++;
                        }
                        result.Sections.Add(section);
                    }
                }
                if (ds.ReportType != ReportType.SummaryCoreRiskRatingsReport)
                {
                    var companyOverviewPageReport = report.Document.Pages[0];
                    LinkCompanyOverviewElements(companyOverviewPageReport, reportSectionGenerators[0].PageElementPositions, _rootOutlinePositions.ChildOutlinePositions);

                    LinkMeiElements(meiPages, reportSectionGenerators[1].PageElementPositions, _rootOutlinePositions.ChildOutlinePositions, true);
                }
            }
            AddPageFooter(report);
            if (report.PageCounter > 0)
            {
                result.PdfDocument = report.Draw();
            }
            return result;
        }

        private void LinkMeiElements(List<Page> meiPages, List<PageElementPosition> pageElementPositions, List<OutlinePosition> outlinePositions, bool isCore)
        {
            try
            {
                var meiDictionary = pageElementPositions.GroupBy(x => x.Name.Split('_')[0]).ToDictionary(x => x.Key, x => x.Select(w => w).ToList());
                foreach (var meiKey in meiDictionary.Keys)
                {
                    var maiPageElementPositionsList = meiDictionary[meiKey];
                    var pageNo = maiPageElementPositionsList.First().PageNumber;
                    var meiName = meiKey;
                    OutlinePosition meiOutlinePosition = null;
                    if(isCore)
                        meiOutlinePosition = outlinePositions.Find(x => x.Name == "Appendix");
                    else
                        meiOutlinePosition = outlinePositions.Find(x => x.Name == "Appendix").ChildOutlinePositions.Find(y => y.Name == meiName);
                    var meiPage = meiPages[pageNo];

                    foreach (PageElementPosition overviewPosition in maiPageElementPositionsList)
                    {
                        OutlinePosition targetOutline = null;
                        if(isCore)
                            targetOutline = meiOutlinePosition.ChildOutlinePositions.FirstOrDefault(x => x.Name.Split('-')[1].Trim() == overviewPosition.Name);
                        else
                            targetOutline = meiOutlinePosition.ChildOutlinePositions.FirstOrDefault(x => meiName + "_" + x.Name.Split('-')[1].Trim() == overviewPosition.Name);
                        if (targetOutline != null)
                        {
                            meiPage.Elements.Add(new Link(overviewPosition.HorizontalPosition,
                                overviewPosition.VerticalPosition, overviewPosition.Width, overviewPosition.Height,
                                    new XYDestination(targetOutline.PageNumber - 1, 0, targetOutline.VerticalPosition)));
                        }
                    }
                }
            }
            catch (Exception ex) { }
        }

        private void LinkCorporateGovernanceElements(Page corporateGovernancePageReport, List<PageElementPosition> pageElementPositions, List<OutlinePosition> outlinePositions)
        {
            try
            {
                var governanceOutlinePosition = outlinePositions.Find(x => x.Name == "Appendix").ChildOutlinePositions.Find(y => y.Name == "Corporate Governance");

                foreach (PageElementPosition overviewPosition in pageElementPositions)
                {
                    var targetOutline = governanceOutlinePosition.ChildOutlinePositions.FirstOrDefault(x => "Corporate Governance_" + x.Name == overviewPosition.Name);
                    if (targetOutline != null)
                    {
                        corporateGovernancePageReport.Elements.Add(new Link(overviewPosition.HorizontalPosition,
                            overviewPosition.VerticalPosition, overviewPosition.Width, overviewPosition.Height,
                                new XYDestination(targetOutline.PageNumber, 0, targetOutline.VerticalPosition)));
                    }
                }
            }
            catch (Exception ex) { }
        }

        private static void LinkCompanyOverviewElements(Page page,
            IEnumerable<PageElementPosition> pageElementPositions, IEnumerable<OutlinePosition> outlinePositions)
        {
            try
            {
                foreach (PageElementPosition overviewPosition in pageElementPositions)
                {
                    var targetOutline = outlinePositions.First(x => x.Name == overviewPosition.Name);

                    page.Elements.Add(new Link(overviewPosition.HorizontalPosition,
                        overviewPosition.VerticalPosition, overviewPosition.Width, overviewPosition.Height,
                            new XYDestination(targetOutline.PageNumber, 0, targetOutline.VerticalPosition)));
                }
            }
            catch (Exception ex) { }
        }

        


        private void AddPageFooter(PdfReport report)
        {
            var pageNumber = 1;
            var pageCount = report.Document.Pages.Count;
            
            float top;
            const float imageTopOffset = 9f;
            const float labelTopOffset = 5f;
            
            const float lineLeftOffset = 80f;
            const float lineWidth = 415f;
            const float imageLeftOffset = 75f;
            const float labelLeftOffset = 460f;

            for (var i = 0; i < report.Document.Pages.Count; i++)
            {
                var page = report.Document.Pages[i];
                float middle = page.Dimensions.Width / 2;

                top = 790f;
                var topMagin = page.Dimensions.TopMargin;
                if (topMagin.NearlyEq(0))
                {
                    top = 810f;
                }

                var leftMagin = page.Dimensions.LeftMargin;

                if (leftMagin.NearlyEq(0))
                {
                    leftMagin = 40f;
                }

                //page.Elements.Add(new Line(leftMagin + lineLeftOffset, top, leftMagin + lineLeftOffset + lineWidth, top, 0.5f, RgbColor.Gray));
                
                page.Elements.Add(new Image(ImageData.GetImage(CompanyLogoFooterImage),
                    leftMagin + imageLeftOffset, top - imageTopOffset+10, 0.24f) { Align = Align.Right, VAlign = VAlign.Top });

                page.Elements.Add(new Label(string.Format("Page {0} of {1}", pageNumber, pageCount),
                    leftMagin + labelLeftOffset, top - labelTopOffset+10, 70, 6, Fonts.RobotoRegular, 6, TextAlign.Right, RgbColor.DimGray));

                page.Elements.Add(new Label(string.Format("© {0} Sustainalytics. All rights reserved.", System.DateTime.Now.Year),
                    leftMagin + middle-100, top - labelTopOffset+10, 160, 6, Fonts.RobotoRegular, 6, TextAlign.Center, RgbColor.DimGray));

                pageNumber++;
            }
        }

    }
}